/*

Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016.  All rights reserved.

Contact:
     SYSTAP, LLC DBA Blazegraph
     2501 Calvert ST NW #106
     Washington, DC 20008
     licenses@blazegraph.com

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*/
/*
 * Created on May 9, 2008
 */

package com.bigdata.counters.linux;

import org.apache.log4j.Logger;

import java.io.File;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * Some utility methods related to integration with <code>sysstat</code>.
 * 
 * @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
 * @version $Id$
 */
public class SysstatUtil {

    final private static Logger log = Logger.getLogger(SysstatUtil.class);

    public interface Options {
        /**
         * The name of the optional property whose value specifies the default
         * location of the SYSSTAT package (pidstat, iostat, etc) (default
         * {@value #DEFAULT_PATH}).
         * 
         * @see #DEFAULT_PATH
         */
        String PATH = "com.bigdata.counters.linux.sysstat.path";
    	
        String DEFAULT_PATH = "/usr/bin";
    }

	/**
	 * Returns the path to the specified sysstat utility (pidstat, sar, etc).
	 * The default is directory is {@value Options#DEFAULT_PATH}. This may be
	 * overridden using the {@value Options#PATH} property. The following
	 * directories are also searched if the program is not found in the
	 * configured default location:
	 * <ul>
	 * <li>/usr/bin</li>
	 * <li>/usr/local/bin</li>
	 * </ul>
	 * 
	 * @return The path to the specified utility. If the utility was not found,
	 *         then the configured path to the utility will be returned anyway.
	 */
    static public final File getPath(final String cmd) {

		File f, path;
        final File configuredPath = path = new File(System.getProperty(
                Options.PATH, Options.DEFAULT_PATH));

        if (log.isInfoEnabled())
            log.info(Options.PATH + "=" + configuredPath);

		if (!(f=new File(path, cmd)).exists() && true) {

			log.warn("Not found: " + f);
			
			path = new File("/usr/bin");
			
			if (!(f = new File(path, cmd)).exists()) {

				log.warn("Not found: " + f);

				path = new File("/usr/local/bin");

				if (!(f = new File(path, cmd)).exists()) {

					log.warn("Not found: " + f);

					log.error("Could not locate: '" + cmd + "'. Set '-D"
							+ Options.PATH + "=<dir>'");

					// restore default even though not found.
					path = configuredPath;
					
				}

			}

		}

		if (configuredPath != path) {

			log.warn("Using effective path: " + Options.PATH + "=" + path);

		}

		return new File(path, cmd);

    }
    
    /**
     * Splits a data line into fields based on whitespace and skipping over the
     * date field (index zero (0) is the index of the first non-date field).
     * <p>
     * Note: Some fields can overflow, e.g., RSS. When this happens the fields
     * in the data lines wind up eating into the whitespace to their
     * <em>right</em>. This means that it is more robust to split the lines
     * based on whitespace once we have skipped over the date field. Since we
     * specify using {@link PIDStatCollector#setEnvironment(Map)} that we want
     * an ISO date format, we know that the date field is 11 characters. The
     * data lines are broken up by whitespace after that.
     * <p>
     * Note: Since we split on whitespace, the resulting strings are already
     * trimmed.
     * 
     * @return The fields.
     */
    static public String[] splitDataLine(final String data) {
        
        // split into fields
        final String[] fields = data.trim().split("\\s+");
        
        if(log.isDebugEnabled()) {
            
            log.debug("fields="+Arrays.toString(fields));
            
        }
    
        return fields;
        
    }

    /**
     * Takes header and data lines and generates a Map<String, String> from them
     *
     *
     * @return a Map where keys are fields names and values are fields values.
     */
    static public Map<String, String> getDataMap(final String header, final String data) {

        Map<String, String> fields = new HashMap<>();

        final String[] header_fields = SysstatUtil.splitDataLine(header);
        final String[] data_fields = SysstatUtil.splitDataLine(data);

        if ( header_fields.length != data_fields.length ) {
            throw new IllegalArgumentException("Different fields count in header and data");
        }

        for (int i = 0; i < header_fields.length; i++) {
            fields.put(header_fields[i], data_fields[i]);
        }

        return fields;

    }
    /**
     * Used to parse the timestamp associated with each row of sysstat output.
     * <p>
     * Note: This assumes that you have controlled the date format using the
     * sysstat ISO date option.
     * <p>
     * Note: This is not thread-safe - use a distinct instance for each
     * {@link PIDStatCollector} or {@link SarCpuUtilizationCollector}.
     * 
     * @deprecated sysstat only reports the TIME OF DAY. In order to get the UTC
     *             time it has to be corrected by the UTC time of the start of
     *             the current day. Since very little latency is expected
     *             between the report by sysstat of its performance counters and
     *             the parsing of those performance counters by our code, it is
     *             MUCH easier and more robust to simply use the current time as
     *             reported by {@link System#currentTimeMillis()}.
     */
    static public DateFormat newDateFormat() {
        
        return new SimpleDateFormat("hh:mm:ss aa");
        
    }

}
